/*******************************************************************************
 * Witz - Winboard Chess Engine                                                *
 * SEE.C                                                                       *
 *                                                                             *
 * A static exchange evaluvator                                                *
 *                                                                             *
 * Please refer to the licence notice in MAIN.C                                *
 *                                                                             *
 * Copywright (C) 2006 Pradu Kannan                                            *
 * http://witz.sf.net/                                                         *
 ******************************************************************************/

#include "build.h"
#include "defs.h"
#include "protos.h"
#include "data.h"

//picks the correct occupancy function to use for the diagnoal across [from][target]
U8 (*occupancy[64][64])(const board* pos, const char square);
//SEEdir[from][target][occupancy] gives the pointer to the ray to find the next attacker
//so it gives a ray in the direction target-->from starting from the square after target
//This might be a little confusing (putting target to the right and from to the left
//but I found it easier to write SEE when using this format
//ray[64][64][256] is in [from][to][occ] format if you would rather use that
char SEEdir[64][64][256];
//has either a rook or a bishop depending on the direction
char SEEpiece[64][64];

/*SEE()
 *a single-square accurate static exchange evaluvation with x-ray attacks
 *it handles all types of moves including promotion, castling, enpassant, captures
 *and normal moves
 *It is decently fast
 *
 *@param pos - the board position
 *@param m   - the move to be evaluvated
 */
int SEE(const board* pos,move m)
{
	int seeScores[32],target=extractTo(m);
	char count=1,from=extractFrom(m),activePiece=extractPiece(m),raycast;
	U64 temp, temp2, attacks = activePiece==P?attacksTo(*pos,target):attacksTo(*pos,target)^toBit[from];
	*seeScores=PieceVal[extractCapture(m)]+PieceVal[extractPromotion(m)];
	if(extractSpecial(m))
	{
		if(activePiece==P) //EP
		{
			*seeScores=PieceVal[P];
			//Ray attacks from EP square target+pawnEPset[2]
			raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
			if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[R])) //queen and rook attacks
					attacks^=toBit[raycast];
		}
		else
			return 0; //no SEE for a castling move
	}
	else
	{
		raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
		if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[SEEpiece[from][target]])) //generate ray attacks
			attacks^=toBit[raycast];
	}

	while(temp=attacks&pos->PiecesSide[pos->xside])
	{
		//xside moves
		if(temp2=temp&(pos->Pieces[P]))
		{
			seeScores[count]=seeScores[count-1]-PieceVal[activePiece];
			activePiece=P;
			count++;
			attacks^=toBit[from=LastOne(temp2)];
			raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
			if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[B])) //generate ray attacks
				attacks^=toBit[raycast];
		}
		else if(temp2=temp&(pos->Pieces[N]))
		{
			seeScores[count]=seeScores[count-1]-PieceVal[activePiece];
			activePiece=N;
			count++;
			attacks^=toBit[LastOne(temp2)];
		}
		else if(temp2=temp&(pos->Pieces[B]))
		{
			seeScores[count]=seeScores[count-1]-PieceVal[activePiece];
			activePiece=B;
			count++;
			attacks^=toBit[from=LastOne(temp2)];
			raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
			if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[B])) //generate ray attacks
				attacks^=toBit[raycast];
		}
		else if(temp2=temp&(pos->Pieces[R]))
		{
			seeScores[count]=seeScores[count-1]-PieceVal[activePiece];
			activePiece=R;
			count++;
			attacks^=toBit[from=LastOne(temp2)];
			raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
			if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[R])) //generate ray attacks
				attacks^=toBit[raycast];
		}
		else if(temp2=temp&(pos->Pieces[Q]))
		{
			seeScores[count]=seeScores[count-1]-PieceVal[activePiece];
			activePiece=Q;
			count++;
			attacks^=toBit[from=LastOne(temp2)];
			raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
			if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[SEEpiece[from][target]])) //generate ray attacks
				attacks^=toBit[raycast];
		}
		else if(temp2=temp&(pos->Pieces[K]))
		{
			seeScores[count]=seeScores[count-1]-PieceVal[activePiece];
			activePiece=K;
			attacks^=toBit[from=LastOne(temp2)];
			if(attacks&(pos->PiecesSide[pos->side]))
			{
				count++;;
				seeScores[count]=Kval;
			}
			goto end;
		}

		//side moves (identical code to xside moves except the PieceVal is added to seeScores)
		if(temp=attacks&(pos->PiecesSide[pos->side]))
		{
			if(temp2=temp&(pos->Pieces[P]))
			{
				seeScores[count]=seeScores[count-1]+PieceVal[activePiece];
				activePiece=P;
				count++;
				attacks^=toBit[from=LastOne(temp2)];
				raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
				if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[B])) //generate ray attacks
					attacks^=toBit[raycast];
			}
			else if(temp2=temp&(pos->Pieces[N]))
			{
				seeScores[count]=seeScores[count-1]+PieceVal[activePiece];
				activePiece=N;
				count++;
				attacks^=toBit[LastOne(temp2)];
			}
			else if(temp2=temp&(pos->Pieces[B]))
			{
				seeScores[count]=seeScores[count-1]+PieceVal[activePiece];
				activePiece=B;
				count++;
				attacks^=toBit[from=LastOne(temp2)];
				raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
				if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[B])) //generate ray attacks
					attacks^=toBit[raycast];
			}
			else if(temp2=temp&(pos->Pieces[R]))
			{
				seeScores[count]=seeScores[count-1]+PieceVal[activePiece];
				activePiece=R;
				count++;
				attacks^=toBit[from=LastOne(temp2)];
				raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
				if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[R])) //generate ray attacks
					attacks^=toBit[raycast];
			}
			else if(temp2=temp&(pos->Pieces[Q]))
			{
				seeScores[count]=seeScores[count-1]+PieceVal[activePiece];
				activePiece=Q;
				count++;
				attacks^=toBit[from=LastOne(temp2)];
				raycast=SEEdir[from][target][(*occupancy[from][target])(pos,from)];
				if(raycast>=0 && toBit[raycast]&(pos->Pieces[Q]|pos->Pieces[SEEpiece[from][target]])) //generate ray attacks
					attacks^=toBit[raycast];
			}
			else if(temp2=temp&(pos->Pieces[K]))
			{
				seeScores[count]=seeScores[count-1]+PieceVal[activePiece];
				activePiece=K;
				count++;
				attacks^=toBit[from=LastOne(temp2)];
				if(attacks&(pos->PiecesSide[pos->xside]))
				{
					count++;
					seeScores[count]=-Kval;
				}
				goto end;
			}
		}
		else
			break;
	}

	//minimax through the scores
	count--;
end:
	if(count%2)
	{
		seeScores[count-1]=min(seeScores[count-1],seeScores[count]);
		count--;
	}
	while(count)
	{
		seeScores[count-1]=max(seeScores[count-1],seeScores[count]);
		count--;
		seeScores[count-1]=min(seeScores[count-1],seeScores[count]);
		count--;
	}
	return *seeScores;
}

/*SEEoccxxxx
 *gives the occupancy across the line of action xxxx for a square
 *
 *@param pos - the board position
 *@param square - the square for which the line of action is needed
 */
U8 SEEocca1h8(const board* pos, const char square)	{return (((U8)(pos->R45>>a1h8shift[square])) & 0xFF);}
U8 SEEocch1a8(const board* pos, const char square)	{return (((U8)(pos->L45>>h1a8shift[square])) & 0xFF);}
U8 SEEoccrank(const board* pos, const char square)	{return (((U8)(pos->AllPieces>>rankshift[square])) & 0xFF);}
U8 SEEoccfile(const board* pos, const char square)	{return (((U8)(pos->L90>>fileshift[square])) & 0xFF);}
U8 SEEoccdummy(const board* pos, const char square)	{return 0;}


/*MLVLVA()
 *a most valuable victom least valuable attacker ordering scheme
 *
 *@param m   - the move to be evaluvated
 */
int MVVLVA(move m)
{
	char cap;
	if(cap=extractCapture(m))
		return extractCapture(m)-extractPiece(m);
	return 0;
}

/*fastSEE()
 *A less accurate version of SEE which dosen't do ray attacks or capture generation
 *For non-captures, it just checks if the square being moved to is under attack
 *if it is under attack it returs the accurate SEEscore.
 *For capture, it checks if the square being move to is under attack
 *if it is under attack it returns the MLV/LVA score, otherwise it returns the value of
 *the attacked piece
 *
 *@param pos - the board position
 *@param m   - the move to be evaluvated
 */
int fastSEE(const board* pos,move m)
{
	if(extractCapture(m)) //if move is a capture
	{
		if(isAttacked(*pos,extractTo(m),pos->side)) //if the square is attacked
			return extractCapture(m)-extractPiece(m); //return MVV/LVA score
		else //return the value of the piece attacked
			return extractPiece(m);
	}
	else
		return 0;
}
